import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { ErrorHandler, NgModule, Optional, SkipSelf } from '@angular/core';

import { SharedModule } from '../shared/shared.module';
import { BreadcrumbComponent } from './components/breadcrumb/breadcrumb.component';
import { ComposeMessageComponent } from './components/compose-message/compose-message.component';
import { PageNotFoundComponent } from './components/page-not-found/page-not-found.component';
import { SidebarComponent } from './components/sidebar/sidebar.component';
import { TopbarComponent } from './components/topbar/topbar.component';
import { AuthenticationGuardService } from './guards/authentication-guard.service';
import { throwIfAlreadyLoaded } from './guards/module-import-guard.service';
import { AuthenticationService } from './services/authentication.service';
import { CommonService } from './services/common.service';
import { DialogService } from './services/dialog.service';
import { EncryptionService } from './services/encryption.service';
import { GlobalErrorHandlerService } from './services/global-error-handler.service';
import { HttpInterceptorService } from './services/http-interceptor.service';
import { HttpRequestService } from './services/http-request.service';
import { MaterialDialogService } from './services/material-dialog.service';
import { MessageService } from './services/message.service';
import { PendingHttpRequestService } from './services/pending-http-request.service';
import { TokenService } from './services/token.service';
import { ValidatorFactoryService } from './services/validator-factory.service';
import { WebSocketService } from './services/web-socket.service';
import { SelectivePreloadingStrategy } from './shared/selective-preloading-strategy';

// Real-world apps have more to worry about. They can have several single-use components (such as spinners, 
//     message toasts, and modal dialogs) that appear only in the AppComponent template. 
//     You don't import them elsewhere so they're not shared in that sense. 

// An NgModule is a class decorated with @NgModule metadata. The metadata do the following:

// - Declare which components, directives, and pipes belong to the module.
// - Make some of those classes public so that other component templates can use them.
// - Import other modules with the components, directives, and pipes needed by the components in this module.
// - Provide services at the application level that any application component can use.

@NgModule({
    imports: [
        // Pure service modules that don't have public (exported) declarations. For example, there's no point in re-exporting 
        // HttpModule because it doesn't export anything. It's only purpose is to add http service providers to the application as a whole.
        // When you import a module, Angular adds the module's service providers (the contents of its providers list) to the application root injector.
        // By adding the HttpModule once, every application component can make http requests.
        // HttpModule,
        // IMPORTANT!!! InMemoryWebApiModule will cause HttpClient error when used crossed module. Also will make HttpInterceptorService failed.
        HttpClientModule,
        SharedModule
    ],
    // The subset of declarations that should be visible and usable in the COMPONENT TEMPLATES of other modules.
    // You export the TopbarComponent so other modules that import the CoreModule can include it in their component templates.
    // All other declared classes are private and hidden by default from the rest of the application.
    // If a component will be only navigated, it doesn't need a selector. And also there's NO reason to make it public via exports.
    exports: [
        TopbarComponent,
        BreadcrumbComponent,
        SidebarComponent,
        // SpinnerComponent
    ],
    // The view classes that belong to this module. Angular has three kinds of view classes: components, directives, and pipes.
    // Only modules can be added to module's imports. Components, directives and pipes are placed in module's declarations.
    // Never re-declare classes that belong to another module.
    // For example: Do not add NgModel—or the FORMS_DIRECTIVES—to the current module metadata's declarations. These directives belong to the FormsModule.
    // Components, directives, and pipes belong to one module only.
    declarations: [
        // Even PageNotFoundComponent & ComposeMessageComponent are not used via selector, but they are referenced in the AppRoutingModule, you still need to put them here.
        PageNotFoundComponent,
        ComposeMessageComponent,
        TopbarComponent,
        BreadcrumbComponent,
        SidebarComponent
    ],
    // Services will be available for entire module.
    // Also a module can add providers to the application's root dependency injector, making those services available everywhere in the application.
    // Creators of services that this module contributes to the global collection of services; they become accessible in all parts of the app.
    // IMPORTANT!!!
    // But it only works if the HeroesModule is placed in the @NgModule.imports of AppModule. 
    // If you are using lazy loading and expecting HeroService is available cross entire application, it will NOT work. 
    // You have to place the HeroService to the @NgModule.providers in AppModule.
    providers: [
        AuthenticationService,
        AuthenticationGuardService,
        // DefaultRequestOptions,
        CommonService,
        DialogService,
        EncryptionService,
        HttpRequestService,
        PendingHttpRequestService,
        SelectivePreloadingStrategy,
        MaterialDialogService,
        MessageService,
        TokenService,
        ValidatorFactoryService,
        WebSocketService,
        {
            provide: HTTP_INTERCEPTORS,
            useClass: HttpInterceptorService,
            multi: true   // Note the multi: true option. This is required and tells Angular that HTTP_INTERCEPTORS is an array of values, rather than a single value.
        },
        {
            provide: ErrorHandler,
            useClass: GlobalErrorHandlerService
        }
    ]
})
export class CoreModule {
    constructor(@Optional() @SkipSelf() parentModule: CoreModule) {
        throwIfAlreadyLoaded(parentModule, 'CoreModule');
    }
}